/*
* BlockingClient.cpp
*
*  Created on: 2013. 7. 23.
*      Author: ncanis
*/
#include "Stdafx.h"
#include "NonBlockingClient.h"
#include <process.h> // thread

namespace TED {


	NonBlockingClient::NonBlockingClient(SocketHandler* handler, const TCHAR* ip, const int port): AbstractSocket(handler,ip,port) {
	}

	NonBlockingClient::~NonBlockingClient() {
	}



	IoSession* NonBlockingClient::Connect() {
		/**
		 * gethostbyname  : 도메인 주소를 ip주소로 변경
		 * inet_aton: ip 주소를 in_addr 구조체로 반환
		 * inet_addr: ip주소를 네트웍 바이트오더의 바이너리 데이터로 리턴한다.
		 * inet_network : ip주소를 호스트 바이트오더의 바이너리 데이터로 리턴한다.
		 * inet_ntoa : 네트웍 바이트오더의 바이너리를 ip address로 리턴한다.
		 */
		SOCKET socket = Init();

		address.sin_family = AF_INET;		// 주소체계(ipv4) 로설정, PF_INET : 프로토콜 체계
		address.sin_addr.s_addr = Util::ted_inet_addr(ip);		//  long 형 데이터의 바이트 오더를 바꾸어주는 함수, INADDR_ANY=localhost
		address.sin_port = htons(port);		// Host 시스템에서 Network로 short 형 데이터를 보낼 때  바이트 오더를 바꾸어주는 함수

		Logger::Get().Debug(_T("Try to connect=%s:%d"), ip, port);

		int result = connect(socket, (struct sockaddr *) &address, sizeof(address));
		if (result== SOCKET_ERROR) {
			closesocket(socket);
			Logger::Get().ErrorSocket(_T("Fail to Connect address=%s:%d."), ip, port);
			return NULL;
		}else{
			/*TCHAR local_ip[15];
			ZeroMemory(local_ip, 15*sizeof(TCHAR));			*/
			Logger::Get().Debug(_T("Connected Server. server=%s:%d (local port=%d"), ip, port, address.sin_port);
			IoSession& session = MakeSession(socket);
			handler->Open(session);

			/**
			 * 소켓에서 얼마나 테이터가 읽힐수 있는지 알게 해주는 함수 읽을수 있는 바이트 수
			 * 패킷을 빠르게 보낼때는 수치가 정확하지 않으니 주의한다.
			 *FIONBIO:비동기설정, FIONREAD: socket으로 읽을수있는 버퍼크기 가져올때사용,
			 *SIOCATMARK : 소켓으로 부터 out-of-band 데이터가 모두 읽었는지를 확인하기 위해 사용한다.
			 *DWORD blockingMode = 1; // 0:블락킹 모드, 1:넌블록킹 모드
			 */
			//ioctlsocket(socket, FIONBIO, &blockingMode);

			/**
			* http://msdn.microsoft.com/ko-kr/library/kdzttdcb.aspx
			*
			* 1. LPSECURITY_ATTRIBUTES : 보안 디스크립터
			* 2. SIZE_T : 초기 Stack 사이즈
			* 3. unsigned(*start_address)(void*), : 스레드 함수
			* 4. void* arglist : 스레드 인자
			* 5. unsigned initflag : 생성옵션
			* 6. unsigned* thrdaddr : 스레드 id
			*/
			// Thread이기때문에 메모리 할당후 전달해야한다.
			SocketThreadParameter* arg = new SocketThreadParameter;
			arg->socket = this;
			arg->session = &session;
			recvThread = (HANDLE)_beginthreadex(NULL,0, this->StartReceiver0, arg, 0, &recvThreadID);
			return &session;
		}
		return NULL;
	}

	unsigned __stdcall NonBlockingClient::StartReceiver0(void *args) {
		SocketThreadParameter* parameters = (SocketThreadParameter*)args;
		NonBlockingClient* client = (NonBlockingClient*)parameters->socket;
		IoSession& session = *parameters->session;
		client->StartReceiver(session);

		delete parameters;
		return 0;
	}

	void NonBlockingClient::StartReceiver(IoSession& session) {
		while(true) {
			INT16 result = Recv(session);
			if(result==SOCKET_ERROR)
			{
				Error(result, _T("Socket Error. Session will be closed."));
				Close(session);
				break;
			}
			Sleep(10);
		}
	}

}